home *** CD-ROM | disk | FTP | other *** search
- /*
- * @(#)StockTicker.java 1.10 95/10/13 Jim Graham
- *
- * Copyright (c) 1994-1995 Sun Microsystems, Inc. All Rights Reserved.
- *
- * Permission to use, copy, modify, and distribute this software
- * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
- * without fee is hereby granted.
- * Please refer to the file http://java.sun.com/copy_trademarks.html
- * for further important copyright and trademark information and to
- * http://java.sun.com/licensing.html for further important licensing
- * information for the Java (tm) Technology.
- *
- * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
- * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
- * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
- * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
- * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
- *
- * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
- * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
- * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
- * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
- * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
- * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
- * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). SUN
- * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
- * HIGH RISK ACTIVITIES.
- */
-
- import java.applet.Applet;
- import java.util.Vector;
- import java.util.StringTokenizer;
- import java.util.Hashtable;
- import java.awt.Graphics;
- import java.awt.Font;
- import java.awt.FontMetrics;
- import java.awt.Color;
-
- /**
- * A simple class to read a stock quote data stream and display the most
- * recent quotes in a streaming stock ticker.
- *
- * @author Jim Graham
- * @version 1.10, 10/13/95
- */
- public
- class StockTicker extends Applet implements StockWatcher {
- Vector Symbols;
- int numSymbols;
- int delay = 10;
- int fetchdelay = 60;
- int deltat = 50;
- int scrolldelta;
- int scrollPos;
- boolean fudge = false;
-
- QuoteQueue qq = new QuoteQueue();
- float quote[];
- float prevquote[];
- boolean feedError[];
- boolean quoteChange[];
- boolean quoteKnown[];
- boolean prevKnown[];
-
- TickerPaintDaemon daemon;
-
- StockStreamParser ssp;
-
- Font symbolFont;
- Font fractFont;
- Font quoteFont;
- FontMetrics symbolFM;
- FontMetrics fractFM;
- FontMetrics quoteFM;
-
- Color errorColor;
- Color freshColor;
- Color staleColor;
- Color bgColor;
- private static Hashtable colorCache = new Hashtable();
-
- static {
- colorCache.put("red", Color.red);
- colorCache.put("green", Color.green);
- colorCache.put("blue", Color.blue);
- colorCache.put("cyan", Color.cyan);
- colorCache.put("magenta", Color.magenta);
- colorCache.put("yellow", Color.yellow);
- colorCache.put("orange", Color.orange);
- colorCache.put("pink", Color.pink);
- colorCache.put("white", Color.white);
- colorCache.put("lightgray", Color.lightGray);
- colorCache.put("gray", Color.gray);
- colorCache.put("darkgray", Color.darkGray);
- colorCache.put("black", Color.black);
- }
-
- /**
- * Initialize the Applet. Get values from attributes.
- * @see browser.Applet
- */
- public void init() {
- String s;
-
- symbolFont = new Font("Helvetica", Font.BOLD, 18);
- symbolFM = getFontMetrics(symbolFont);
- quoteFont = new Font("Helvetica", Font.PLAIN, 16);
- quoteFM = getFontMetrics(quoteFont);
- fractFont = new Font("Helvetica", Font.PLAIN, 12);
- fractFM = getFontMetrics(fractFont);
-
- s = getParameter("stocks");
- if (s == null)
- s = "SUNW|HWP|SGI|MSFT|INTC|IBM|DEC|CY|ADBE|AAPL|SPX|ZRA";
- Symbols = new Vector(10);
- StringTokenizer st = new StringTokenizer(s, "|");
- scrolldelta = 0;
- while (st.hasMoreTokens()) {
- String stockname = st.nextToken();
- Symbols.addElement(stockname);
- scrolldelta += symbolFM.stringWidth(stockname)
- + quoteFM.stringWidth("99+")
- + fractFM.stringWidth("3/4 1/2")
- + QuoteSnapshot.margin;
- }
- numSymbols = Symbols.size();
- quote = new float[numSymbols];
- prevquote = new float[numSymbols];
- feedError = new boolean[numSymbols];
- quoteChange = new boolean[numSymbols];
- quoteKnown = new boolean[numSymbols];
- prevKnown = new boolean[numSymbols];
-
- s = getParameter("fudge");
- if (s != null) fudge = s.equals("true");
-
- s = getParameter("delay");
- if (s != null) fetchdelay = Integer.parseInt(s);
-
- s = getParameter("scrollt");
- if (s != null)
- delay = Integer.parseInt(s);
- else
- delay = numSymbols;
-
- s = getParameter("deltat");
- if (s != null) deltat = Integer.parseInt(s);
-
- errorColor = getColorAttribute("errorfg", Color.yellow);
- freshColor = getColorAttribute("freshfg", Color.green);
- staleColor = getColorAttribute("stalefg", Color.white);
- bgColor = getColorAttribute("bgcolor", Color.darkGray);
-
- scrolldelta = scrolldelta * deltat / (delay * 1000);
- scrollPos = size().width;
- }
-
- private Color getColorAttribute(String name, Color defcolor) {
- String s = getParameter(name);
- if (s == null)
- return defcolor;
- s = s.toLowerCase();
- defcolor = (Color) colorCache.get(s);
- if (defcolor != null)
- return defcolor;
- int colorval = java.lang.Integer.parseInt(s, 16);
- int r = (colorval >> 16) & 0xff;
- int g = (colorval >> 8) & 0xff;
- int b = colorval & 0xff;
- defcolor = new Color(r, g, b);
- colorCache.put(s, defcolor);
- return defcolor;
- }
-
- /**
- * Run the quote data fetcher.
- * @see java.lang.Thread
- */
- public synchronized void startSSP() {
- ssp = new StockStreamParser(this, getDocumentBase(),
- fudge, fetchdelay, 0);
- String stocks[] = new String[numSymbols];
- Symbols.copyInto(stocks);
- ssp.setStock(stocks);
- ssp.start();
- }
-
- public synchronized void feedEOF(StockStreamParser ssp) {
- if (this.ssp == ssp) {
- this.ssp = null;
- }
- }
-
- int findSymbol(String symbol) {
- int index = Symbols.indexOf(symbol);
- if (index < 0) {
- System.out.println("Unrequested quote received for " + symbol);
- }
- return index;
- }
-
- public void newHigh(String symbol, double value) {
- }
-
- public void newLow(String symbol, double value) {
- }
-
- public void newClose(String symbol, double value) {
- int index = findSymbol(symbol);
- if (index >= 0) {
- prevquote[index] = (float) value;
- prevKnown[index] = true;
- }
- }
-
- public void newQuote(String symbol, double value) {
- int index = findSymbol(symbol);
- if (index >= 0) {
- if (quote[index] != (float) value) {
- quote[index] = (float) value;
- quoteChange[index] = true;
- }
- quoteKnown[index] = true;
- }
- }
-
- public void newQuote(String symbol, double value, int time) {
- System.out.println("Ticker ignoring history of " + value
- + "@" + time +" for " + symbol);
- }
-
- public void setFeedError(String symbol, boolean error) {
- int index = findSymbol(symbol);
- if (index >= 0) {
- feedError[index] = error;
- if (error && !prevKnown[index]) {
- prevquote[index] = quote[index];
- }
- }
- }
-
- public void flushQuotes() {
- }
-
- int nextQuote = 0;
-
- public void scroll() {
- if (qq.getHead() == null) {
- scrollPos = size().width;
- } else {
- scrollPos -= scrolldelta;
- }
- repaint();
- }
-
- private int lastScrollPos;
-
- /**
- * Update the current frame.
- */
- public synchronized void update(Graphics g, int startpos) {
- int pos = scrollPos;
- QuoteSnapshot qs = qq.getHead();
- int fallen = 0;
- while (qs != null) {
- if (pos + qs.width > startpos) {
- pos = qs.paint(g, pos);
- } else {
- pos += qs.width;
- }
- if (pos <= 0) {
- scrollPos = pos;
- fallen++;
- }
- qs = qs.next;
- }
- while (fallen-- > 0) {
- qq.dequeue();
- }
- int numskipped = 0;
- while (pos <= size().width) {
- Color color;
- if (!feedError[nextQuote]
- && (!quoteKnown[nextQuote] || !prevKnown[nextQuote]))
- {
- if (++nextQuote == numSymbols) {
- nextQuote = 0;
- }
- if (++numskipped == numSymbols) {
- break;
- }
- continue;
- }
- numskipped = 0;
- if (feedError[nextQuote]) {
- color = errorColor;
- } else {
- if (quoteChange[nextQuote]) {
- color = freshColor;
- quoteChange[nextQuote] = false;
- } else {
- color = staleColor;
- }
- }
- qs = qq.enqueue(this,
- (String) Symbols.elementAt(nextQuote),
- quote[nextQuote],
- prevquote[nextQuote],
- color);
- if (++nextQuote == numSymbols) {
- nextQuote = 0;
- }
- pos = qs.paint(g, pos);
- }
- lastScrollPos = scrollPos;
- }
-
- /**
- * Update the current frame.
- */
- public synchronized void update(Graphics g) {
- int width = size().width;
- int height = size().height;
- if (qq.getHead() == null) {
- scrollPos = width;
- paint(g);
- } else {
- int delta = lastScrollPos - scrollPos;
- g.copyArea(delta, 0, width - delta, height, -delta, 0);
- g.clipRect(width - delta, 0, delta, height);
- update(g, width - delta);
- }
- }
-
- /**
- * Update the current frame.
- */
- public synchronized void paint(Graphics g) {
- g.setColor(bgColor);
- g.fillRect(0, 0, size().width, size().height);
- update(g, 0);
- }
-
- /**
- * Start the applet by forking an animation thread.
- */
- public synchronized void start() {
- if (ssp == null) {
- startSSP();
- }
- if (daemon == null) {
- daemon = new TickerPaintDaemon(this);
- daemon.start();
- }
- }
-
- /**
- * Stop the applet.
- */
- public synchronized void stop() {
- if (ssp != null) {
- ssp.stop();
- ssp = null;
- }
- if (daemon != null) {
- daemon.stopticker();
- daemon = null;
- }
- }
- }
-
- class TickerPaintDaemon extends Thread {
- StockTicker ticker;
-
- public TickerPaintDaemon(StockTicker ticker) {
- this.ticker = ticker;
- }
-
- public synchronized void run() {
- while (ticker != null && ticker.daemon == this) {
- ticker.scroll();
- try {
- wait(ticker.deltat);
- } catch (InterruptedException e) {
- break;
- }
- }
- }
-
- public synchronized void stopticker() {
- ticker = null;
- notify();
- }
- }
-
- class QuoteSnapshot {
- StockTicker ticker;
- String symbol;
- float quote;
- float prev;
- Color color;
- int width;
- QuoteSnapshot next;
-
- String QuoteWholeStr;
- String QuoteFractStr;
- String DiffWholeStr;
- String DiffFractStr;
-
- static int margin = 15;
-
- public QuoteSnapshot(StockTicker t, String s, float q, float p, Color c) {
- set(t, s, q, p, c);
- }
-
- public void set(StockTicker t, String s, float q, float p, Color c) {
- ticker = t;
- symbol = s;
- quote = q;
- prev = p;
- color = c;
- QuoteWholeStr = formatwhole(quote);
- QuoteFractStr = formatfract(quote);
- DiffWholeStr = formatdiff(quote, prev);
- DiffFractStr = formatdifffract(quote, prev);
- width = margin +
- ticker.symbolFM.stringWidth(symbol) + 1 +
- ticker.quoteFM.stringWidth(QuoteWholeStr) + 1 +
- ticker.fractFM.stringWidth(QuoteFractStr) + 2 +
- ticker.quoteFM.stringWidth(DiffWholeStr) + 1 +
- ticker.fractFM.stringWidth(DiffFractStr);
- }
-
- private String formatwhole(float q) {
- return Float.toString((float) Math.floor(q));
- }
-
- private String formatfract(float q) {
- int numerator = (int) ((q - Math.floor(q)) * 16);
- int denominator = 16;
- while (numerator != 0 && (numerator & 1) == 0) {
- numerator >>= 1;
- denominator >>= 1;
- }
- return (numerator == 0) ? "" : (numerator + "/" + denominator);
- }
-
- private String formatdiff(float now, float before) {
- float diff = now - before;
- if (diff == 0)
- return "";
- int wholediff = (int) Math.floor(Math.abs(diff));
- String s = (diff < 0) ? "-" : "+";
- if (wholediff != 0)
- s = s + wholediff;
- return s;
- }
-
- private String formatdifffract(float now, float before) {
- float diff = now - before;
- return (diff == 0) ? "" : formatfract(Math.abs(diff));
- }
-
- public int paint(Graphics g, int pos) {
- int baseline = ticker.symbolFM.getAscent();
- g.setColor(ticker.bgColor);
- g.fillRect(pos, 0, width, ticker.symbolFM.getHeight());
- g.setColor(color);
- g.setFont(ticker.symbolFont);
- g.drawString(symbol, pos, baseline);
- pos += ticker.symbolFM.stringWidth(symbol) + 1;
- g.setFont(ticker.quoteFont);
- g.drawString(QuoteWholeStr, pos, baseline);
- pos += ticker.quoteFM.stringWidth(QuoteWholeStr) + 1;
- g.setFont(ticker.fractFont);
- g.drawString(QuoteFractStr, pos, baseline);
- pos += ticker.fractFM.stringWidth(QuoteFractStr) + 2;
- g.setFont(ticker.quoteFont);
- g.drawString(DiffWholeStr, pos, baseline);
- pos += ticker.quoteFM.stringWidth(DiffWholeStr) + 1;
- g.setFont(ticker.fractFont);
- g.drawString(DiffFractStr, pos, baseline);
- pos += ticker.fractFM.stringWidth(DiffFractStr) + margin;
- return pos;
- }
- }
-
- class QuoteQueue {
- QuoteSnapshot freeList;
- QuoteSnapshot queueHead;
- QuoteSnapshot queueTail;
-
- public QuoteQueue() {
- }
-
- public QuoteSnapshot enqueue(StockTicker t, String s,
- float q, float p, Color c) {
- QuoteSnapshot qs;
- if (freeList == null) {
- qs = new QuoteSnapshot(t, s, q, p, c);
- } else {
- qs = freeList;
- freeList = qs.next;
- qs.set(t, s, q, p, c);
- }
- qs.next = null;
- if (queueTail == null) {
- queueHead = qs;
- } else {
- queueTail.next = qs;
- }
- queueTail = qs;
- return qs;
- }
-
- public void dequeue() {
- QuoteSnapshot qs = queueHead;
- if (queueTail == qs) {
- queueHead = queueTail = null;
- } else {
- queueHead = qs.next;
- }
- qs.next = freeList;
- freeList = qs;
- }
-
- public QuoteSnapshot getHead() {
- return queueHead;
- }
- }
-